home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-11-24 | 54.3 KB | 2,449 lines |
- Newsgroups: comp.sources.misc
- From: jfh@rpp386.Cactus.ORG (John F Haugh II)
- Subject: v26i057: shadow - Shadow Password Suite, Part04/11
- Message-ID: <1991Nov24.185012.20145@sparky.imd.sterling.com>
- X-Md4-Signature: c03114fe66a9e8b90c57c4303280b825
- Date: Sun, 24 Nov 1991 18:50:12 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: jfh@rpp386.Cactus.ORG (John F Haugh II)
- Posting-number: Volume 26, Issue 57
- Archive-name: shadow/part04
- Environment: UNIX
- Supersedes: shadow-2: Volume 06, Issue 22-24
-
- #! /bin/sh
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: chfn.c gpmain.c newusers.c patchlevel.h sgroupio.c
- # Wrapped by kent@sparky on Sun Nov 24 11:03:41 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 4 (of 11)."'
- if test -f 'chfn.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'chfn.c'\"
- else
- echo shar: Extracting \"'chfn.c'\" \(12441 characters\)
- sed "s/^X//" >'chfn.c' <<'END_OF_FILE'
- X/*
- X * Copyright 1989, 1990, 1991, John F. Haugh II
- X * All rights reserved.
- X *
- X * Permission is granted to copy and create derivative works for any
- X * non-commercial purpose, provided this copyright notice is preserved
- X * in all copies of source code, or included in human readable form
- X * and conspicuously displayed on all copies of object code or
- X * distribution media.
- X */
- X
- X#include <sys/types.h>
- X#include <stdio.h>
- X#include <fcntl.h>
- X#include <signal.h>
- X
- X#ifndef lint
- Xstatic char sccsid[] = "@(#)chfn.c 3.7 10:14:35 8/15/91";
- X#endif
- X
- X/*
- X * Set up some BSD defines so that all the BSD ifdef's are
- X * kept right here
- X */
- X
- X#ifndef BSD
- X#include <string.h>
- X#include <memory.h>
- X#else
- X#include <strings.h>
- X#define strchr index
- X#define strrchr rindex
- X#endif
- X
- X#include "config.h"
- X#include "pwd.h"
- X
- X#ifdef USE_SYSLOG
- X#include <syslog.h>
- X
- X#ifndef LOG_WARN
- X#define LOG_WARN LOG_WARNING
- X#endif
- X#endif
- X
- X/*
- X * Global variables.
- X */
- X
- Xchar *Progname;
- Xchar user[BUFSIZ];
- Xchar fullnm[BUFSIZ];
- Xchar roomno[BUFSIZ];
- Xchar workph[BUFSIZ];
- Xchar homeph[BUFSIZ];
- Xchar slop[BUFSIZ];
- Xint amroot;
- X
- X/*
- X * External identifiers
- X */
- X
- Xextern int optind;
- Xextern char *optarg;
- Xextern struct passwd *getpwuid ();
- Xextern struct passwd *getpwnam ();
- Xextern char *getlogin ();
- X#ifdef NDBM
- Xextern int pw_dbm_mode;
- X#endif
- X
- X/*
- X * #defines for messages. This facilities foreign language conversion
- X * since all messages are defined right here.
- X */
- X
- X#define USAGE \
- X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n"
- X#define ADMUSAGE \
- X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
- X [ -h home_ph ] [ -o other ] [ user ]\n"
- X#define NOPERM "%s: Permission denied.\n"
- X#define WHOAREYOU "%s: Cannot determine you user name.\n"
- X#define INVALID_NAME "%s: invalid name: \"%s\"\n"
- X#define INVALID_ROOM "%s: invalid room number: \"%s\"\n"
- X#define INVALID_WORKPH "%s: invalid work phone: \"%s\"\n"
- X#define INVALID_HOMEPH "%s: invalid home phone: \"%s\"\n"
- X#define INVALID_OTHER "%s: \"%s\" contains illegal characters\n"
- X#define INVALID_FIELDS "%s: fields too long\n"
- X#define NEWFIELDSMSG "Changing the user information for %s\n"
- X#define NEWFIELDSMSG2 \
- X"Enter the new value, or press return for the default\n\n"
- X#define NEWNAME "Full Name"
- X#define NEWROOM "Room Number"
- X#define NEWWORKPHONE "Work Phone"
- X#define NEWHOMEPHONE "Home Phone"
- X#define NEWSLOP "Other"
- X#define UNKUSER "%s: Unknown user %s\n"
- X#define PWDBUSY "Cannot lock the password file; try again later.\n"
- X#define PWDBUSY2 "can't lock /etc/passwd\n"
- X#define OPNERROR "Cannot open the password file.\n"
- X#define OPNERROR2 "can't open /etc/passwd\n"
- X#define UPDERROR "Error updating the password entry.\n"
- X#define UPDERROR2 "error updating passwd entry\n"
- X#define DBMERROR "Error updating the DBM password entry.\n"
- X#define DBMERROR2 "error updating DBM passwd entry.\n"
- X#define NOTROOT "Cannot change ID to root.\n"
- X#define NOTROOT2 "can't setuid(0).\n"
- X#define CLSERROR "Cannot commit password file changes.\n"
- X#define CLSERROR2 "can't rewrite /etc/passwd.\n"
- X#define UNLKERROR "Cannot unlock the password file.\n"
- X#define UNLKERROR2 "can't unlock /etc/passwd.\n"
- X#define CHGGECOS "changed user `%s' information.\n"
- X
- X/*
- X * usage - print command line syntax and exit
- X */
- X
- Xvoid
- Xusage ()
- X{
- X fprintf (stderr, amroot ? USAGE:ADMUSAGE, Progname);
- X exit (1);
- X}
- X
- X/*
- X * new_fields - change the user's GECOS information interactively
- X *
- X * prompt the user for each of the four fields and fill in the fields
- X * from the user's response, or leave alone if nothing was entered.
- X */
- X
- Xnew_fields ()
- X{
- X printf (NEWFIELDSMSG2);
- X
- X change_field (fullnm, NEWNAME);
- X change_field (roomno, NEWROOM);
- X change_field (workph, NEWWORKPHONE);
- X change_field (homeph, NEWHOMEPHONE);
- X
- X if (amroot)
- X change_field (slop, NEWSLOP);
- X}
- X
- X/*
- X * copy_field - get the next field from the gecos field
- X *
- X * copy_field copies the next field from the gecos field, returning a
- X * pointer to the field which follows, or NULL if there are no more
- X * fields.
- X */
- X
- Xchar *
- Xcopy_field (in, out, extra)
- Xchar *in; /* the current GECOS field */
- Xchar *out; /* where to copy the field to */
- Xchar *extra; /* fields with '=' get copied here */
- X{
- X char *cp;
- X
- X while (in) {
- X if (cp = strchr (in, ','))
- X *cp++ = '\0';
- X
- X if (! strchr (in, '='))
- X break;
- X
- X if (extra) {
- X if (extra[0])
- X strcat (extra, ",");
- X
- X strcat (extra, in);
- X }
- X in = cp;
- X }
- X if (in && out)
- X strcpy (out, in);
- X
- X return cp;
- X}
- X
- X/*
- X * chfn - change a user's password file information
- X *
- X * This command controls the GECOS field information in the
- X * password file entry.
- X *
- X * The valid options are
- X *
- X * -f full name
- X * -r room number
- X * -w work phone number
- X * -h home phone number
- X * -o other information (*)
- X *
- X * (*) requires root permission to execute.
- X */
- X
- Xint
- Xmain (argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X char *cp; /* temporary character pointer */
- X struct passwd *pw; /* password file entry */
- X struct passwd pwent; /* modified password file entry */
- X char old_gecos[BUFSIZ]; /* buffer for old GECOS fields */
- X char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */
- X int flag; /* flag currently being processed */
- X int fflg = 0; /* -f - set full name */
- X int rflg = 0; /* -r - set room number */
- X int wflg = 0; /* -w - set work phone number */
- X int hflg = 0; /* -h - set home phone number */
- X int oflg = 0; /* -o - set other information */
- X int i; /* loop control variable */
- X
- X /*
- X * This command behaves different for root and non-root
- X * users.
- X */
- X
- X amroot = getuid () == 0;
- X#ifdef NDBM
- X pw_dbm_mode = O_RDWR;
- X#endif
- X
- X /*
- X * Get the program name. The program name is used as a
- X * prefix to most error messages. It is also used as input
- X * to the openlog() function for error logging.
- X */
- X
- X if (Progname = strrchr (argv[0], '/'))
- X Progname++;
- X else
- X Progname = argv[0];
- X
- X#ifdef USE_SYSLOG
- X openlog (Progname, LOG_PID, LOG_AUTH);
- X#endif
- X
- X /*
- X * The remaining arguments will be processed one by one and
- X * executed by this command. The name is the last argument
- X * if it does not begin with a "-", otherwise the name is
- X * determined from the environment and must agree with the
- X * real UID. Also, the UID will be checked for any commands
- X * which are restricted to root only.
- X */
- X
- X while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
- X switch (flag) {
- X case 'f':
- X fflg++;
- X strcpy (fullnm, optarg);
- X break;
- X case 'r':
- X rflg++;
- X strcpy (roomno, optarg);
- X break;
- X case 'w':
- X wflg++;
- X strcpy (workph, optarg);
- X break;
- X case 'h':
- X hflg++;
- X strcpy (homeph, optarg);
- X break;
- X case 'o':
- X if (amroot) {
- X oflg++;
- X strcpy (slop, optarg);
- X break;
- X }
- X fprintf (stderr, NOPERM, Progname);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X default:
- X usage ();
- X }
- X }
- X
- X /*
- X * Get the name of the user to check. It is either
- X * the command line name, or the name getlogin()
- X * returns.
- X */
- X
- X if (optind < argc) {
- X strncpy (user, argv[optind], sizeof user);
- X pw = getpwnam (user);
- X } else if (cp = getlogin ()) {
- X strncpy (user, cp, sizeof user);
- X pw = getpwnam (user);
- X } else {
- X fprintf (stderr, WHOAREYOU, Progname);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X
- X /*
- X * Make certain there was a password entry for the
- X * user.
- X */
- X
- X if (! pw) {
- X fprintf (stderr, UNKUSER, Progname, user);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X
- X /*
- X * Non-privileged users are only allowed to change the
- X * shell if the UID of the user matches the current
- X * real UID.
- X */
- X
- X if (! amroot && pw->pw_uid != getuid ()) {
- X fprintf (stderr, NOPERM, Progname);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X
- X /*
- X * Make a copy of the user's password file entry so it
- X * can be modified without worrying about it be modified
- X * elsewhere.
- X */
- X
- X pwent = *pw;
- X pwent.pw_name = strdup (pw->pw_name);
- X pwent.pw_passwd = strdup (pw->pw_passwd);
- X#ifdef ATT_AGE
- X pwent.pw_age = strdup (pw->pw_age);
- X#endif
- X#ifdef ATT_COMMENT
- X pwent.pw_comment = strdup (pw->pw_comment);
- X#endif
- X pwent.pw_dir = strdup (pw->pw_dir);
- X pwent.pw_shell = strdup (pw->pw_shell);
- X
- X /*
- X * Now get the full name. It is the first comma separated field
- X * in the GECOS field.
- X */
- X
- X strcpy (old_gecos, pw->pw_gecos);
- X cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop);
- X
- X /*
- X * Now get the room number. It is the next comma separated field,
- X * if there is indeed one.
- X */
- X
- X if (cp)
- X cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
- X
- X /*
- X * Now get the work phone number. It is the third field.
- X */
- X
- X if (cp)
- X cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
- X
- X /*
- X * Now get the home phone number. It is the fourth field.
- X */
- X
- X if (cp)
- X cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
- X
- X /*
- X * Anything left over is "slop".
- X */
- X
- X if (cp) {
- X if (slop[0])
- X strcat (slop, ",");
- X
- X strcat (slop, cp);
- X }
- X
- X /*
- X * If none of the fields were changed from the command line,
- X * let the user interactively change them.
- X */
- X
- X if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg) {
- X printf (NEWFIELDSMSG, user);
- X new_fields ();
- X }
- X
- X /*
- X * Check all of the fields for valid information
- X */
- X
- X if (valid_field (fullnm, ":,=")) {
- X fprintf (stderr, INVALID_NAME, Progname, fullnm);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X if (valid_field (roomno, ":,=")) {
- X fprintf (stderr, INVALID_ROOM, Progname, roomno);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X if (valid_field (workph, ":,=")) {
- X fprintf (stderr, INVALID_WORKPH, Progname, workph);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X if (valid_field (homeph, ":,=")) {
- X fprintf (stderr, INVALID_HOMEPH, Progname, homeph);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X if (valid_field (slop, ":")) {
- X fprintf (stderr, INVALID_OTHER, Progname, slop);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X
- X /*
- X * Build the new GECOS field by plastering all the pieces together,
- X * if they will fit ...
- X */
- X
- X if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
- X strlen (homeph) + strlen (slop) > 80) {
- X fprintf (stderr, INVALID_FIELDS, Progname);
- X#ifdef USE_SYSLOG
- X closelog ();
- X#endif
- X exit (1);
- X }
- X sprintf (new_gecos, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
- X if (slop[0]) {
- X strcat (new_gecos, ",");
- X strcat (new_gecos, slop);
- X }
- X pwent.pw_gecos = new_gecos;
- X pw = &pwent;
- X
- X /*
- X * Before going any further, raise the ulimit to prevent
- X * colliding into a lowered ulimit, and set the real UID
- X * to root to protect against unexpected signals. Any
- X * keyboard signals are set to be ignored.
- X */
- X
- X ulimit (2, 30000);
- X if (setuid (0)) {
- X fprintf (stderr, NOTROOT);
- X#ifdef USE_SYSLOG
- X syslog (LOG_ERR, NOTROOT2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X signal (SIGHUP, SIG_IGN);
- X signal (SIGINT, SIG_IGN);
- X signal (SIGQUIT, SIG_IGN);
- X#ifdef SIGTSTP
- X signal (SIGTSTP, SIG_IGN);
- X#endif
- X
- X /*
- X * The passwd entry is now ready to be committed back to
- X * the password file. Get a lock on the file and open it.
- X */
- X
- X for (i = 0;i < 30;i++)
- X if (pw_lock ())
- X break;
- X
- X if (i == 30) {
- X fprintf (stderr, PWDBUSY);
- X#ifdef USE_SYSLOG
- X syslog (LOG_WARN, PWDBUSY2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X if (! pw_open (O_RDWR)) {
- X fprintf (stderr, OPNERROR);
- X (void) pw_unlock ();
- X#ifdef USE_SYSLOG
- X syslog (LOG_ERR, OPNERROR2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X
- X /*
- X * Update the passwd file entry. If there is a DBM file,
- X * update that entry as well.
- X */
- X
- X if (! pw_update (pw)) {
- X fprintf (stderr, UPDERROR);
- X (void) pw_unlock ();
- X#ifdef USE_SYSLOG
- X syslog (LOG_ERR, UPDERROR2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X#if defined(DBM) || defined(NDBM)
- X if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
- X fprintf (stderr, DBMERROR);
- X (void) pw_unlock ();
- X#ifdef USE_SYSLOG
- X syslog (LOG_ERR, DBMERROR2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X endpwent ();
- X#endif
- X
- X /*
- X * Changes have all been made, so commit them and unlock the
- X * file.
- X */
- X
- X if (! pw_close ()) {
- X fprintf (stderr, CLSERROR);
- X (void) pw_unlock ();
- X#ifdef USE_SYSLOG
- X syslog (LOG_ERR, CLSERROR2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X if (! pw_unlock ()) {
- X fprintf (stderr, UNLKERROR);
- X#ifdef USE_SYSLOG
- X syslog (LOG_ERR, UNLKERROR2);
- X closelog ();
- X#endif
- X exit (1);
- X }
- X#ifdef USE_SYSLOG
- X syslog (LOG_INFO, CHGGECOS, user);
- X closelog ();
- X#endif
- X exit (0);
- X}
- END_OF_FILE
- if test 12441 -ne `wc -c <'chfn.c'`; then
- echo shar: \"'chfn.c'\" unpacked with wrong size!
- fi
- # end of 'chfn.c'
- fi
- if test -f 'gpmain.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'gpmain.c'\"
- else
- echo shar: Extracting \"'gpmain.c'\" \(12316 characters\)
- sed "s/^X//" >'gpmain.c' <<'END_OF_FILE'
- X/*
- X * Copyright 1990, 1991, John F. Haugh II
- X * All rights reserved.
- X *
- X * Permission is granted to copy and create derivative works for any
- X * non-commercial purpose, provided this copyright notice is preserved
- X * in all copies of source code, or included in human readable form
- X * and conspicuously displayed on all copies of object code or
- X * distribution media.
- X */
- X
- X#include <sys/types.h>
- X#include <stdio.h>
- X#include "pwd.h"
- X#include "shadow.h"
- X#include <grp.h>
- X#include <fcntl.h>
- X#include <signal.h>
- X#include <errno.h>
- X#ifndef BSD
- X#include <termio.h>
- X#ifdef SYS3
- X#include <sys/ioctl.h>
- X#endif
- X#include <string.h>
- X#ifndef SYS3
- X#include <memory.h>
- X#endif
- X#else
- X#include <sgtty.h>
- X#include <strings.h>
- X#define strchr index
- X#define strrchr rindex
- X#endif
- X#include "config.h"
- X
- X#if !defined(BSD) || !defined(SUN)
- X#define bzero(p,l) memset(p, 0, l)
- X#endif
- X
- X#ifndef lint
- Xstatic char _sccsid[] = "@(#)gpmain.c 3.10 07:44:08 9/17/91";
- X#endif
- X
- Xchar name[BUFSIZ];
- Xchar pass[BUFSIZ];
- Xchar pass2[BUFSIZ];
- X
- Xstruct group grent;
- X
- Xchar *Prog;
- Xchar *user;
- Xchar *group;
- Xint aflg;
- Xint dflg;
- Xint rflg;
- Xint Rflg;
- X
- X#ifndef RETRIES
- X#define RETRIES 3
- X#endif
- X
- Xextern char *l64a ();
- Xextern char *crypt ();
- Xextern char *pw_encrypt ();
- Xextern int errno;
- Xextern long a64l ();
- Xextern void entry ();
- Xextern time_t time ();
- Xextern char *malloc ();
- Xextern char *getpass ();
- X#ifdef NDBM
- Xextern int sg_dbm_mode;
- Xextern int gr_dbm_mode;
- X#endif
- X
- X/*
- X * usage - display usage message
- X */
- X
- Xvoid
- Xusage ()
- X{
- X fprintf (stderr, "usage: %s [ -r|R ] group\n", Prog);
- X fprintf (stderr, " %s [ -a user ] group\n", Prog);
- X fprintf (stderr, " %s [ -d user ] group\n", Prog);
- X exit (1);
- X}
- X
- X/*
- X * add_list - add a member to a list of group members
- X *
- X * the array of member names is searched for the new member
- X * name, and if not present it is added to a freshly allocated
- X * list of users.
- X */
- X
- Xchar **
- Xadd_list (list, member)
- Xchar **list;
- Xchar *member;
- X{
- X int i;
- X char **tmp;
- X
- X for (i = 0;list[i] != (char *) 0;i++)
- X if (strcmp (list[i], member) == 0)
- X return list;
- X
- X if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
- X return 0;
- X
- X for (i = 0;list[i] != (char *) 0;i++)
- X tmp[i] = list[i];
- X
- X tmp[i++] = strdup (member);
- X tmp[i] = (char *) 0;
- X
- X return tmp;
- X}
- X
- X/*
- X * del_list - delete a group member from a list of members
- X *
- X * del_list searches a list of group members, copying the
- X * members which do not match "member" to a newly allocated
- X * list.
- X */
- X
- Xchar **
- Xdel_list (list, member)
- Xchar **list;
- Xchar *member;
- X{
- X int i, j;
- X char **tmp;
- X
- X for (j = i = 0;list[i] != (char *) 0;i++)
- X if (strcmp (list[i], member))
- X j++;
- X
- X tmp = (char **) malloc ((j + 1) * sizeof member);
- X
- X for (j = i = 0;list[i] != (char *) 0;i++)
- X if (strcmp (list[i], member) != 0)
- X tmp[j++] = list[i];
- X
- X tmp[j] = (char *) 0;
- X
- X return tmp;
- X}
- X
- Xint
- Xmain (argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X extern int optind;
- X extern char *optarg;
- X int flag;
- X int i;
- X void die ();
- X char *cp;
- X char *getlogin ();
- X char *getpass ();
- X int amroot;
- X int retries;
- X int ruid = getuid();
- X struct group *gr = 0;
- X struct group *getgrnam ();
- X struct group *sgetgrent ();
- X struct sgrp *sg = 0;
- X struct sgrp sgent;
- X struct sgrp *getsgnam ();
- X struct passwd *pw = 0;
- X struct passwd *getpwuid ();
- X struct passwd *getpwnam ();
- X
- X /*
- X * Make a note of whether or not this command was invoked
- X * by root. This will be used to bypass certain checks
- X * later on. Also, set the real user ID to match the
- X * effective user ID. This will prevent the invoker from
- X * issuing signals which would interfer with this command.
- X */
- X
- X amroot = getuid () == 0;
- X#ifdef NDBM
- X sg_dbm_mode = O_RDWR;
- X gr_dbm_mode = O_RDWR;
- X#endif
- X setuid (geteuid ());
- X Prog = argv[0];
- X setbuf (stdout, (char *) 0);
- X setbuf (stderr, (char *) 0);
- X
- X while ((flag = getopt (argc, argv, "a:d:grR")) != EOF) {
- X switch (flag) {
- X case 'a': /* add a user */
- X aflg++;
- X user = optarg;
- X break;
- X case 'd': /* delete a user */
- X dflg++;
- X user = optarg;
- X break;
- X case 'g': /* no-op from normal password */
- X break;
- X case 'r': /* remove group password */
- X rflg++;
- X break;
- X case 'R': /* restrict group password */
- X Rflg++;
- X break;
- X default:
- X usage ();
- X }
- X }
- X
- X /*
- X * Make sure exclusive flags are exclusive
- X */
- X
- X if (aflg + dflg + rflg + Rflg > 1)
- X usage ();
- X
- X /*
- X * Unless the mode is -a, -d or -r, the input and output must
- X * both be a tty. The typical keyboard signals are caught
- X * so the termio modes can be restored.
- X */
- X
- X if (! aflg && ! dflg && ! rflg && ! Rflg) {
- X if (! isatty (0) || ! isatty (1))
- X exit (1);
- X
- X die (0); /* save tty modes */
- X
- X signal (SIGHUP, die);
- X signal (SIGINT, die);
- X signal (SIGQUIT, die);
- X signal (SIGTERM, die);
- X }
- X
- X /*
- X * Determine the name of the user that invoked this command.
- X * This is really hit or miss because there are so many ways
- X * that command can be executed and so many ways to trip up
- X * the routines that report the user name.
- X */
- X
- X if ((cp = getlogin ()) && (pw = getpwnam (cp)) && pw->pw_uid == ruid) {
- X /* need user name */
- X (void) strcpy (name, cp);
- X } else if (pw = getpwuid (ruid)) /* get it from password file */
- X strcpy (name, pw->pw_name);
- X else { /* can't find user name! */
- X fprintf (stderr, "Who are you?\n");
- X exit (1);
- X }
- X if (! (pw = getpwnam (name)))
- X goto failure; /* can't get my name ... */
- X
- X /*
- X * Get the name of the group that is being affected. The group
- X * entry will be completely replicated so it may be modified
- X * later on.
- X */
- X
- X if (! (group = argv[optind]))
- X usage ();
- X
- X if (! (gr = getgrnam (group))) {
- X fprintf (stderr, "unknown group: %s\n", group);
- X exit (1);
- X }
- X grent = *gr;
- X grent.gr_name = strdup (gr->gr_name);
- X grent.gr_passwd = strdup (gr->gr_passwd);
- X
- X for (i = 0;gr->gr_mem[i];i++)
- X ;
- X grent.gr_mem = (char **) malloc ((i + 1) * sizeof (char *));
- X for (i = 0;gr->gr_mem[i];i++)
- X grent.gr_mem[i] = strdup (gr->gr_mem[i]);
- X grent.gr_mem[i] = (char *) 0;
- X#ifdef SHADOWGRP
- X if (sg = getsgnam (group)) {
- X sgent = *sg;
- X sgent.sg_name = strdup (sg->sg_name);
- X sgent.sg_passwd = strdup (sg->sg_name);
- X
- X for (i = 0;sg->sg_mem[i];i++)
- X ;
- X sgent.sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
- X for (i = 0;sg->sg_mem[i];i++)
- X sgent.sg_mem[i] = strdup (sg->sg_mem[i]);
- X sgent.sg_mem[i] = 0;
- X
- X for (i = 0;sg->sg_adm[i];i++)
- X ;
- X sgent.sg_adm = (char **) malloc (sizeof (char *) * (i + 1));
- X for (i = 0;sg->sg_adm[i];i++)
- X sgent.sg_adm[i] = strdup (sg->sg_adm[i]);
- X sgent.sg_adm[i] = 0;
- X } else {
- X sgent.sg_name = strdup (group);
- X sgent.sg_passwd = grent.gr_passwd;
- X grent.gr_passwd = "!";
- X
- X for (i = 0;grent.gr_mem[i];i++)
- X ;
- X sgent.sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
- X for (i = 0;grent.gr_mem[i];i++)
- X sgent.sg_mem[i] = strdup (grent.gr_mem[i]);
- X sgent.sg_mem[i] = 0;
- X
- X sgent.sg_adm = (char **) malloc (sizeof (char *) * 2);
- X if (sgent.sg_mem[0]) {
- X sgent.sg_adm[0] = strdup (sgent.sg_mem[0]);
- X sgent.sg_adm[1] = 0;
- X } else
- X sgent.sg_adm[0] = 0;
- X
- X sg = &sgent;
- X }
- X#endif
- X
- X /*
- X * The policy for changing a group is that 1) you must be root
- X * or 2) you must be the first listed member of the group. The
- X * first listed member of a group can do anything to that group
- X * that the root user can.
- X */
- X
- X if (! amroot) {
- X if (grent.gr_mem[0] == (char *) 0)
- X goto failure;
- X
- X if (strcmp (grent.gr_mem[0], name) != 0)
- X goto failure;
- X }
- X
- X /*
- X * Removing a password is straight forward. Just set the
- X * password field to a "".
- X */
- X
- X if (rflg) {
- X grent.gr_passwd = "";
- X#ifdef SHADOWGRP
- X sgent.sg_passwd = "";
- X#endif
- X goto output;
- X } else if (Rflg) {
- X grent.gr_passwd = "!";
- X#ifdef SHADOWGRP
- X sgent.sg_passwd = "!";
- X#endif
- X goto output;
- X }
- X
- X /*
- X * Adding a member to a member list is pretty straightforward
- X * as well. Call the appropriate routine and split.
- X */
- X
- X if (aflg) {
- X if (getpwnam (user) == (struct passwd *) 0) {
- X fprintf (stderr, "%s: unknown user %s\n", Prog, user);
- X exit (1);
- X }
- X printf ("Adding user %s to group %s\n", user, group);
- X grent.gr_mem = add_list (grent.gr_mem, user);
- X#ifdef SHADOWGRP
- X sgent.sg_mem = add_list (sgent.sg_mem, user);
- X#endif
- X goto output;
- X }
- X
- X /*
- X * Removing a member from the member list is the same deal
- X * as adding one, except the routine is different.
- X */
- X
- X if (dflg) {
- X int removed = 0;
- X
- X for (i = 0;grent.gr_mem[i];i++)
- X if (strcmp (user, grent.gr_mem[i]) == 0)
- X break;
- X
- X printf ("Removing user %s from group %s\n", user, group);
- X
- X if (grent.gr_mem[i] != (char *) 0) {
- X removed = 1;
- X grent.gr_mem = del_list (grent.gr_mem, user);
- X }
- X#ifdef SHADOWGRP
- X for (i = 0;sgent.sg_mem[i];i++)
- X if (strcmp (user, sgent.sg_mem[i]) == 0)
- X break;
- X
- X if (sgent.sg_mem[i] != (char *) 0) {
- X removed = 1;
- X sgent.sg_mem = del_list (sgent.sg_mem, user);
- X }
- X#endif
- X if (! removed) {
- X fprintf (stderr, "%s: unknown member %s\n", Prog, user);
- X exit (1);
- X }
- X goto output;
- X }
- X
- X /*
- X * A new password is to be entered and it must be encrypted,
- X * etc. The password will be prompted for twice, and both
- X * entries must be identical. There is no need to validate
- X * the old password since the invoker is either the group
- X * owner, or root.
- X */
- X
- X printf ("Changing the password for group %s\n", group);
- X
- X for (retries = 0;retries < RETRIES;retries++) {
- X if (! (cp = getpass ("New Password:")))
- X exit (1);
- X else {
- X strcpy (pass, cp);
- X bzero (cp, strlen (cp));
- X }
- X if (! (cp = getpass ("Re-enter new password:")))
- X exit (1);
- X else {
- X strcpy (pass2, cp);
- X bzero (cp, strlen (cp));
- X }
- X if (strcmp (pass, pass2) == 0)
- X break;
- X
- X bzero (pass, sizeof pass);
- X bzero (pass2, sizeof pass2);
- X
- X if (retries + 1 < RETRIES)
- X puts ("They don't match; try again");
- X }
- X bzero (pass2, sizeof pass2);
- X
- X if (retries == RETRIES) {
- X fprintf (stderr, "%s: Try again later\n", Prog);
- X exit (1);
- X }
- X#ifdef SHADOWGRP
- X sgent.sg_passwd = pw_encrypt (pass, (char *) 0);
- X#else
- X grent.gr_passwd = pw_encrypt (pass, (char *) 0);
- X#endif
- X bzero (pass, sizeof pass);
- X
- X /*
- X * This is the common arrival point to output the new group
- X * file. The freshly crafted entry is in allocated space.
- X * The group file will be locked and opened for writing. The
- X * new entry will be output, etc.
- X */
- X
- Xoutput:
- X signal (SIGHUP, SIG_IGN);
- X signal (SIGINT, SIG_IGN);
- X signal (SIGQUIT, SIG_IGN);
- X
- X if (! gr_lock ()) {
- X fprintf (stderr, "%s: can't get lock\n", Prog);
- X exit (1);
- X }
- X#ifdef SHADOWGRP
- X if (! sgr_lock ()) {
- X fprintf (stderr, "%s: can't get shadow lock\n", Prog);
- X exit (1);
- X }
- X#endif
- X if (! gr_open (O_RDWR)) {
- X fprintf (stderr, "%s: can't open file\n", Prog);
- X exit (1);
- X }
- X#ifdef SHADOWGRP
- X if (! sgr_open (O_RDWR)) {
- X fprintf (stderr, "%s: can't open shadow file\n", Prog);
- X exit (1);
- X }
- X#endif
- X if (! gr_update (&grent)) {
- X fprintf (stderr, "%s: can't update entry\n", Prog);
- X exit (1);
- X }
- X#ifdef SHADOWGRP
- X if (! sgr_update (&sgent)) {
- X fprintf (stderr, "%s: can't update shadow entry\n", Prog);
- X exit (1);
- X }
- X#endif
- X if (! gr_close ()) {
- X fprintf (stderr, "%s: can't re-write file\n", Prog);
- X exit (1);
- X }
- X#ifdef SHADOWGRP
- X if (! sgr_close ()) {
- X fprintf (stderr, "%s: can't re-write shadow file\n", Prog);
- X exit (1);
- X }
- X (void) sgr_unlock ();
- X#endif
- X if (! gr_unlock ()) {
- X fprintf (stderr, "%s: can't unlock file\n", Prog);
- X exit (1);
- X }
- X#ifdef NDBM
- X if (access ("/etc/group.pag", 0) == 0 && ! gr_dbm_update (&grent)) {
- X fprintf (stderr, "%s: can't update DBM files\n", Prog);
- X exit (1);
- X }
- X endgrent ();
- X#ifdef SHADOWGRP
- X if (access ("/etc/gshadow.pag", 0) == 0 && ! sgr_dbm_update (&sgent)) {
- X fprintf (stderr, "%s: can't update DBM shadow files\n", Prog);
- X exit (1);
- X }
- X endsgent ();
- X#endif
- X#endif
- X exit (0);
- X /*NOTREACHED*/
- X
- Xfailure:
- X fprintf (stderr, "Permission denied.\n");
- X exit (1);
- X /*NOTREACHED*/
- X}
- X
- X/*
- X * die - set or reset termio modes.
- X *
- X * die() is called before processing begins. signal() is then
- X * called with die() as the signal handler. If signal later
- X * calls die() with a signal number, the terminal modes are
- X * then reset.
- X */
- X
- Xvoid die (killed)
- Xint killed;
- X{
- X#ifdef BSD
- X static struct sgtty sgtty;
- X
- X if (killed)
- X stty (0, &sgtty);
- X else
- X gtty (0, &sgtty);
- X#else
- X static struct termio sgtty;
- X
- X if (killed)
- X ioctl (0, TCSETA, &sgtty);
- X else
- X ioctl (0, TCGETA, &sgtty);
- X#endif
- X if (killed) {
- X putchar ('\n');
- X fflush (stdout);
- X exit (killed);
- X }
- X}
- END_OF_FILE
- if test 12316 -ne `wc -c <'gpmain.c'`; then
- echo shar: \"'gpmain.c'\" unpacked with wrong size!
- fi
- # end of 'gpmain.c'
- fi
- if test -f 'newusers.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'newusers.c'\"
- else
- echo shar: Extracting \"'newusers.c'\" \(13144 characters\)
- sed "s/^X//" >'newusers.c' <<'END_OF_FILE'
- X/*
- X * Copyright 1990, 1991, John F. Haugh II
- X * All rights reserved.
- X *
- X * Permission is granted to copy and create derivative works for any
- X * non-commercial purpose, provided this copyright notice is preserved
- X * in all copies of source code, or included in human readable form
- X * and conspicuously displayed on all copies of object code or
- X * distribution media.
- X *
- X * newusers - create users from a batch file
- X *
- X * newusers creates a collection of entries in /etc/passwd
- X * and related files by reading a passwd-format file and
- X * adding entries in the related directories.
- X */
- X
- X#include <stdio.h>
- X#include "pwd.h"
- X#include <grp.h>
- X#include <fcntl.h>
- X#include <string.h>
- X#include "config.h"
- X#ifdef SHADOWPWD
- X#include "shadow.h"
- X#endif
- X
- X#ifndef lint
- Xstatic char sccsid[] = "@(#)newusers.c 3.5 07:44:18 9/17/91";
- X#endif
- X
- Xchar *Prog;
- X
- Xextern char *pw_encrypt();
- Xextern char *malloc();
- X
- Xint pw_lock(), gr_lock();
- Xint pw_open(), gr_open();
- Xstruct passwd *pw_locate(), *pw_next();
- Xstruct group *gr_locate(), *gr_next();
- Xint pw_update(), gr_update();
- Xint pw_close(), gr_close();
- Xint pw_unlock(), gr_unlock();
- Xextern int getdef_num();
- X
- X#ifdef SHADOWPWD
- Xint spw_lock(), spw_open(), spw_update(), spw_close(), spw_unlock();
- Xstruct spwd *spw_locate(), *spw_next();
- X#endif
- X
- X#ifndef MKDIR
- X
- X/*
- X * mkdir - for those of us with no mkdir() system call.
- X */
- X
- Xmkdir (dir, mode)
- Xchar *dir;
- Xint mode;
- X{
- X int mask;
- X int status;
- X int pid;
- X int i;
- X
- X mode = (~mode & 0777);
- X mask = umask (mode);
- X if ((pid = fork ()) == 0) {
- X execl ("/bin/mkdir", "mkdir", dir, (char *) 0);
- X perror ("/bin/mkdir");
- X _exit (1);
- X } else {
- X while ((i = wait (&status)) != pid && i != -1)
- X ;
- X }
- X umask (mask);
- X return status;
- X}
- X#endif
- X
- X/*
- X * usage - display usage message and exit
- X */
- X
- Xusage ()
- X{
- X fprintf (stderr, "Usage: %s [ input ]\n", Prog);
- X exit (1);
- X}
- X
- X/*
- X * add_group - create a new group or add a user to an existing group
- X */
- X
- Xint
- Xadd_group (name, gid, ngid)
- Xchar *name;
- Xchar *gid;
- Xint *ngid;
- X{
- X struct passwd *pwd;
- X struct group *grp;
- X struct group grent;
- X char *members[2];
- X int i;
- X
- X /*
- X * Start by seeing if the named group already exists. This
- X * will be very easy to deal with if it does.
- X */
- X
- X if (grp = gr_locate (gid)) {
- Xadd_member:
- X grent = *grp;
- X *ngid = grent.gr_gid;
- X for (i = 0;grent.gr_mem[i] != (char *) 0;i++)
- X if (strcmp (grent.gr_mem[i], name) == 0)
- X return 0;
- X
- X if (! (grent.gr_mem = (char **)
- X malloc (sizeof (char *) * (i + 2)))) {
- X fprintf (stderr, "%s: Out of Memory\n", Prog);
- X return -1;
- X }
- X memcpy (grent.gr_mem, grp->gr_mem, sizeof (char *) * (i + 2));
- X grent.gr_mem[i] = strdup (name);
- X grent.gr_mem[i + 1] = (char *) 0;
- X
- X return ! gr_update (&grent);
- X }
- X
- X /*
- X * The group did not exist, so I try to figure out what the
- X * GID is going to be. The gid parameter is probably "", meaning
- X * I figure out the GID from the password file. I want the UID
- X * and GID to match, unless the GID is already used.
- X */
- X
- X if (gid[0] == '\0') {
- X i = 100;
- X for (pw_rewind ();pwd = pw_next ();) {
- X if (pwd->pw_uid >= i)
- X i = pwd->pw_uid + 1;
- X }
- X for (gr_rewind ();grp = gr_next ();) {
- X if (grp->gr_gid == i) {
- X i = -1;
- X break;
- X }
- X }
- X } else if (gid[0] >= '0' && gid[0] <= '9') {
- X
- X /*
- X * The GID is a number, which means either this is a brand new
- X * group, or an existing group. For existing groups I just add
- X * myself as a member, just like I did earlier.
- X */
- X
- X i = atoi (gid);
- X for (gr_rewind ();grp = gr_next ();)
- X if (grp->gr_gid == i)
- X goto add_member;
- X } else
- X
- X /*
- X * The last alternative is that the GID is a name which is not
- X * already the name of an existing group, and I need to figure
- X * out what group ID that group name is going to have.
- X */
- X
- X i = -1;
- X
- X /*
- X * If I don't have a group ID by now, I'll go get the
- X * next one.
- X */
- X
- X if (i == -1) {
- X for (i = 100, gr_rewind ();grp = gr_next ();)
- X if (grp->gr_gid >= i)
- X i = grp->gr_gid + 1;
- X }
- X
- X /*
- X * Now I have all of the fields required to create the new
- X * group.
- X */
- X
- X if (gid[0] && (gid[0] <= '0' || gid[0] >= '9'))
- X grent.gr_name = gid;
- X else
- X grent.gr_name = name;
- X
- X grent.gr_passwd = "!";
- X grent.gr_gid = i;
- X members[0] = name;
- X members[1] = (char *) 0;
- X grent.gr_mem = members;
- X
- X *ngid = grent.gr_gid;
- X return ! gr_update (&grent);
- X}
- X
- X/*
- X * add_user - create a new user ID
- X */
- X
- Xadd_user (name, uid, nuid, gid)
- Xchar *name;
- Xchar *uid;
- Xint *nuid;
- Xint gid;
- X{
- X struct passwd *pwd;
- X struct passwd pwent;
- X int i;
- X
- X /*
- X * The first guess for the UID is either the numerical UID
- X * that the caller provided, or the next available UID.
- X */
- X
- X if (uid[0] >= '0' && uid[0] <= '9') {
- X i = atoi (uid);
- X } if (uid[0] && (pwd = pw_locate (uid))) {
- X i = pwd->pw_uid;
- X } else {
- X i = 100;
- X for (pw_rewind ();pwd = pw_next ();)
- X if (pwd->pw_uid >= i)
- X i = pwd->pw_uid + 1;
- X }
- X
- X /*
- X * I don't want to fill in the entire password structure
- X * members JUST YET, since there is still more data to be
- X * added. So, I fill in the parts that I have.
- X */
- X
- X pwent.pw_name = name;
- X pwent.pw_passwd = "!";
- X#ifdef ATT_AGE
- X pwent.pw_age = "";
- X#endif
- X#ifdef ATT_COMMENT
- X pwent.pw_comment = "";
- X#endif
- X#ifdef BSD_QUOTAS
- X pwent.pw_quota = 0;
- X#endif
- X pwent.pw_uid = i;
- X pwent.pw_gid = gid;
- X pwent.pw_gecos = "";
- X pwent.pw_dir = "";
- X pwent.pw_shell = "";
- X
- X *nuid = i;
- X return ! pw_update (&pwent);
- X}
- X
- X/*
- X * add_passwd - add or update the encrypted password
- X */
- X
- Xadd_passwd (pwd, passwd)
- Xstruct passwd *pwd;
- Xchar *passwd;
- X{
- X#ifdef SHADOWPWD
- X struct spwd *sp;
- X struct spwd spent;
- X#endif
- X static char newage[5];
- X extern char *l64a();
- X
- X /*
- X * In the case of regular password files, this is real
- X * easy - pwd points to the entry in the password file.
- X * Shadow files are harder since there are zillions of
- X * things to do ...
- X */
- X
- X#ifndef SHADOWPWD
- X pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
- X#ifdef ATT_AGE
- X if (strlen (pwd->pw_age) == 4) {
- X strcpy (newage, pwd->pw_age);
- X strcpy (newage + 2,
- X l64a (time ((long *) 0) / (7L*24L*3600L)));
- X pwd->pw_age = newage;
- X }
- X#endif /* ATT_AGE */
- X return 0;
- X#else
- X
- X /*
- X * Do the first and easiest shadow file case. The user
- X * already exists in the shadow password file.
- X */
- X
- X if (sp = spw_locate (pwd->pw_name)) {
- X spent = *sp;
- X spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
- X return ! spw_update (sp);
- X }
- X
- X /*
- X * Pick the next easiest case - the user has an encrypted
- X * password which isn't equal to "!". The password was set
- X * to "!" earlier when the entry was created, so this user
- X * would have to have had the password set someplace else.
- X */
- X
- X if (strcmp (pwd->pw_passwd, "!") != 0) {
- X pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
- X#ifdef ATT_AGE
- X if (strlen (pwd->pw_age) == 4) {
- X strcpy (newage, pwd->pw_age);
- X strcpy (newage + 2,
- X l64a (time ((long *) 0) / (7L*24L*3600L)));
- X pwd->pw_age = newage;
- X }
- X#endif /* ATT_AGE */
- X return 0;
- X }
- X
- X /*
- X * Now the really hard case - I need to create an entirely
- X * shadow password file entry.
- X */
- X
- X spent.sp_namp = pwd->pw_name;
- X spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
- X spent.sp_lstchg = time ((long *) 0) / (24L*3600L);
- X spent.sp_min = getdef_num("PASS_MIN_DAYS", 0);
- X /* 10000 is infinity this week */
- X spent.sp_max = getdef_num("PASS_MAX_DAYS", 10000);
- X spent.sp_warn = getdef_num("PASS_WARN_AGE", -1);
- X spent.sp_inact = -1;
- X spent.sp_expire = -1;
- X spent.sp_flag = -1;
- X
- X return ! spw_update (&spent);
- X#endif
- X}
- X
- Xmain (argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X char buf[BUFSIZ];
- X char *fields[8];
- X int nfields;
- X char *cp;
- X#ifdef SHADOWPWD
- X struct spwd *spw_locate();
- X#endif
- X struct passwd *pw;
- X struct passwd newpw;
- X struct passwd *pw_locate();
- X int errors = 0;
- X int line = 0;
- X int uid;
- X int gid;
- X int i;
- X
- X if (Prog = strrchr (argv[0], '/'))
- X Prog++;
- X else
- X Prog = argv[0];
- X
- X if (argc > 1 && argv[1][0] == '-')
- X usage ();
- X
- X if (argc == 2) {
- X if (! freopen (argv[1], "r", stdin)) {
- X sprintf (buf, "%s: %s", Prog, argv[1]);
- X perror (buf);
- X exit (1);
- X }
- X }
- X
- X /*
- X * Lock the password files and open them for update. This will
- X * bring all of the entries into memory where they may be
- X * searched for an modified, or new entries added. The password
- X * file is the key - if it gets locked, assume the others can
- X * be locked right away.
- X */
- X
- X for (i = 0;i < 30;i++) {
- X if (pw_lock ())
- X break;
- X }
- X if (i == 30) {
- X fprintf (stderr, "%s: can't lock /etc/passwd.\n", Prog);
- X exit (1);
- X }
- X#ifdef SHADOWPWD
- X if (! spw_lock () || ! gr_lock ())
- X#else
- X if (! gr_lock ())
- X#endif
- X {
- X fprintf (stderr, "%s: can't lock files, try again later\n",
- X Prog);
- X (void) pw_unlock ();
- X#ifdef SHADOWPWD
- X (void) spw_unlock ();
- X#endif
- X exit (1);
- X }
- X#ifdef SHADOWPWD
- X if (! pw_open (O_RDWR) || ! spw_open (O_RDWR) || ! gr_open (O_RDWR))
- X#else
- X if (! pw_open (O_RDWR) || ! gr_open (O_RDWR))
- X#endif
- X {
- X fprintf (stderr, "%s: can't open files\n", Prog);
- X (void) pw_unlock ();
- X#ifdef SHADOWPWD
- X (void) spw_unlock ();
- X#endif
- X (void) gr_unlock ();
- X exit (1);
- X }
- X
- X /*
- X * Read each line. The line has the same format as a password
- X * file entry, except that certain fields are not contrained to
- X * be numerical values. If a group ID is entered which does
- X * not already exist, an attempt is made to allocate the same
- X * group ID as the numerical user ID. Should that fail, the
- X * next available group ID over 100 is allocated. The pw_gid
- X * field will be updated with that value.
- X */
- X
- X while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
- X line++;
- X if (cp = strrchr (buf, '\n')) {
- X *cp = '\0';
- X } else {
- X fprintf (stderr, "%s: line %d: line too long\n",
- X Prog, line);
- X errors++;
- X continue;
- X }
- X
- X /*
- X * Break the string into fields and screw around with
- X * them. There MUST be 7 colon separated fields,
- X * although the values aren't that particular.
- X */
- X
- X for (cp = buf, nfields = 0;nfields < 7;nfields++) {
- X fields[nfields] = cp;
- X if (cp = strchr (cp, ':'))
- X *cp++ = '\0';
- X else
- X break;
- X }
- X if (*cp || nfields != 6) {
- X fprintf (stderr, "%s: line %d: invalid line\n",
- X Prog, line);
- X continue;
- X }
- X
- X /*
- X * Now the fields are processed one by one. The first
- X * field to be processed is the group name. A new
- X * group will be created if the group name is non-numeric
- X * and does not already exist. The named user will be
- X * the only member. If there is no named group to be a
- X * member of, the UID will be figured out and that value
- X * will be a candidate for a new group, if that group ID
- X * exists, a whole new group ID will be made up.
- X */
- X
- X if (! (pw = pw_locate (fields[0])) &&
- X add_group (fields[0], fields[3], &gid)) {
- X fprintf (stderr, "%s: %d: can't create GID\n",
- X Prog, line);
- X errors++;
- X continue;
- X }
- X
- X /*
- X * Now we work on the user ID. It has to be specified
- X * either as a numerical value, or left blank. If it
- X * is a numerical value, that value will be used, otherwise
- X * the next available user ID is computed and used. After
- X * this there will at least be a (struct passwd) for the
- X * user.
- X */
- X
- X if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
- X fprintf (stderr, "%s: line %d: can't create UID\n",
- X Prog, line);
- X errors++;
- X continue;
- X }
- X
- X /*
- X * The password, gecos field, directory, and shell fields
- X * all come next.
- X */
- X
- X if (! (pw = pw_locate (fields[0]))) {
- X fprintf (stderr, "%s: line %d: cannot find user %s\n",
- X Prog, line, fields[0]);
- X errors++;
- X continue;
- X }
- X newpw = *pw;
- X
- X if (add_passwd (&newpw, fields[1])) {
- X fprintf (stderr, "%s: line %d: can't update password\n",
- X Prog, line);
- X errors++;
- X continue;
- X }
- X if (fields[4][0])
- X newpw.pw_gecos = fields[4];
- X
- X if (fields[5][0])
- X newpw.pw_dir = fields[5];
- X
- X if (fields[6][0])
- X newpw.pw_shell = fields[6];
- X
- X if (newpw.pw_dir[0] && access (newpw.pw_dir, 0)) {
- X if (mkdir (newpw.pw_dir,
- X 0777 & ~getdef_num("UMASK", 0)))
- X fprintf (stderr, "%s: line %d: mkdir failed\n",
- X Prog, line);
- X else if (chown (newpw.pw_dir,
- X newpw.pw_uid, newpw.pw_gid))
- X fprintf (stderr, "%s: line %d: chown failed\n",
- X Prog, line);
- X }
- X
- X /*
- X * Update the password entry with the new changes made.
- X */
- X
- X if (! pw_update (&newpw)) {
- X fprintf (stderr, "%s: line %d: can't update entry\n",
- X Prog, line);
- X errors++;
- X continue;
- X }
- X }
- X
- X /*
- X * Any detected errors will cause the entire set of changes
- X * to be aborted. Unlocking the password file will cause
- X * all of the changes to be ignored. Otherwise the file is
- X * closed, causing the changes to be written out all at
- X * once, and then unlocked afterwards.
- X */
- X
- X if (errors) {
- X fprintf (stderr, "%s: error detected, changes ignored\n", Prog);
- X (void) gr_unlock ();
- X#ifdef SHADOWPWD
- X (void) spw_unlock ();
- X#endif
- X (void) pw_unlock ();
- X exit (1);
- X }
- X#ifdef SHADOWPWD
- X if (! pw_close () || ! spw_close () || ! gr_close ())
- X#else
- X if (! pw_close () || ! gr_close ())
- X#endif
- X {
- X fprintf (stderr, "%s: error updating files\n", Prog);
- X (void) gr_unlock ();
- X#ifdef SHADOWPWD
- X (void) spw_unlock ();
- X#endif
- X (void) pw_unlock ();
- X exit (1);
- X }
- X (void) gr_unlock ();
- X#ifdef SHADOWPWD
- X (void) spw_unlock ();
- X#endif
- X (void) pw_unlock ();
- X
- X exit (0);
- X /*NOTREACHED*/
- X}
- END_OF_FILE
- if test 13144 -ne `wc -c <'newusers.c'`; then
- echo shar: \"'newusers.c'\" unpacked with wrong size!
- fi
- # end of 'newusers.c'
- fi
- if test -f 'patchlevel.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'patchlevel.h'\"
- else
- echo shar: Extracting \"'patchlevel.h'\" \(431 characters\)
- sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
- X/*
- X * Copyright 1991, John F. Haugh II
- X * All rights reserved.
- X *
- X * Permission is granted to copy and create derivative works for any
- X * non-commercial purpose, provided this copyright notice is preserved
- X * in all copies of source code, or included in human readable form
- X * and conspicuously displayed on all copies of object code or
- X * distribution media.
- X */
- X
- X#define RELEASE 3
- X#define PATCHLEVEL 13
- X#define VERSION "3.1.0"
- END_OF_FILE
- if test 431 -ne `wc -c <'patchlevel.h'`; then
- echo shar: \"'patchlevel.h'\" unpacked with wrong size!
- fi
- # end of 'patchlevel.h'
- fi
- if test -f 'sgroupio.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'sgroupio.c'\"
- else
- echo shar: Extracting \"'sgroupio.c'\" \(11654 characters\)
- sed "s/^X//" >'sgroupio.c' <<'END_OF_FILE'
- X/*
- X * Copyright 1990, 1991, John F. Haugh II
- X * All rights reserved.
- X *
- X * Permission is granted to copy and create derivative works for any
- X * non-commercial purpose, provided this copyright notice is preserved
- X * in all copies of source code, or included in human readable form
- X * and conspicuously displayed on all copies of object code or
- X * distribution media.
- X *
- X * This file implements a transaction oriented shadow group
- X * database library. The shadow group file is updated one
- X * entry at a time. After each transaction the file must be
- X * logically closed and transferred to the existing shadow
- X * group file. The sequence of events is
- X *
- X * sgr_lock -- lock shadow group file
- X * sgr_open -- logically open shadow group file
- X * while transaction to process
- X * sgr_(locate,update,remove) -- perform transaction
- X * done
- X * sgr_close -- commit transactions
- X * sgr_unlock -- remove shadow group lock
- X */
- X
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <fcntl.h>
- X#include <errno.h>
- X#include <stdio.h>
- X#ifdef BSD
- X#include <strings.h>
- X#define strchr index
- X#define strrchr rindex
- X#else
- X#include <string.h>
- X#endif
- X#include "shadow.h"
- X
- X#ifndef lint
- Xstatic char sccsid[] = "@(#)sgroupio.c 3.6 09:10:42 7/17/91";
- X#endif
- X
- Xstatic int islocked;
- Xstatic int isopen;
- Xstatic int open_modes;
- Xstatic FILE *sgrfp;
- X
- Xstruct sg_file_entry {
- X char *sgr_line;
- X int sgr_changed;
- X struct sgrp *sgr_entry;
- X struct sg_file_entry *sgr_next;
- X};
- X
- Xstatic struct sg_file_entry *sgr_head;
- Xstatic struct sg_file_entry *sgr_tail;
- Xstatic struct sg_file_entry *sgr_cursor;
- Xstatic int sgr_changed;
- Xstatic int lock_pid;
- X
- X#define SG_LOCK "/etc/gshadow.lock"
- X#define GR_TEMP "/etc/gshadow.%d"
- X#define SGROUP "/etc/gshadow"
- X
- Xstatic char sg_filename[BUFSIZ] = SGROUP;
- X
- Xextern char *strdup();
- Xextern struct sgrp *sgetsgent();
- Xextern char *fgetsx();
- Xextern char *malloc();
- X
- X/*
- X * sgr_dup - duplicate a shadow group file entry
- X *
- X * sgr_dup() accepts a pointer to a shadow group file entry and
- X * returns a pointer to a shadow group file entry in allocated memory.
- X */
- X
- Xstatic struct sgrp *
- Xsgr_dup (sgrent)
- Xstruct sgrp *sgrent;
- X{
- X struct sgrp *sgr;
- X int i;
- X
- X if (! (sgr = (struct sgrp *) malloc (sizeof *sgr)))
- X return 0;
- X
- X if ((sgr->sg_name = strdup (sgrent->sg_name)) == 0 ||
- X (sgr->sg_passwd = strdup (sgrent->sg_passwd)) == 0)
- X return 0;
- X
- X for (i = 0;sgrent->sg_mem[i];i++)
- X ;
- X
- X sgr->sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
- X for (i = 0;sgrent->sg_mem[i];i++)
- X if (! (sgr->sg_mem[i] = strdup (sgrent->sg_mem[i])))
- X return 0;
- X
- X sgr->sg_mem[i] = 0;
- X
- X sgr->sg_adm = (char **) malloc (sizeof (char *) * (i + 1));
- X for (i = 0;sgrent->sg_adm[i];i++)
- X if (! (sgr->sg_adm[i] = strdup (sgrent->sg_adm[i])))
- X return 0;
- X
- X sgr->sg_adm[i] = 0;
- X
- X return sgr;
- X}
- X
- X/*
- X * sgr_free - free a dynamically allocated shadow group file entry
- X *
- X * sgr_free() frees up the memory which was allocated for the
- X * pointed to entry.
- X */
- X
- Xstatic void
- Xsgr_free (sgrent)
- Xstruct sgrp *sgrent;
- X{
- X int i;
- X
- X free (sgrent->sg_name);
- X free (sgrent->sg_passwd);
- X
- X for (i = 0;sgrent->sg_mem[i];i++)
- X free (sgrent->sg_mem[i]);
- X
- X free (sgrent->sg_mem);
- X
- X for (i = 0;sgrent->sg_adm[i];i++)
- X free (sgrent->sg_adm[i]);
- X
- X free (sgrent->sg_adm);
- X}
- X
- X/*
- X * sgr_name - change the name of the shadow group file
- X */
- X
- Xint
- Xsgr_name (name)
- Xchar *name;
- X{
- X if (isopen || strlen (name) > (BUFSIZ-10))
- X return -1;
- X
- X strcpy (sg_filename, name);
- X return 0;
- X}
- X
- X/*
- X * sgr_lock - lock a shadow group file
- X *
- X * sgr_lock() encapsulates the lock operation. it returns
- X * TRUE or FALSE depending on the shadow group file being
- X * properly locked. the lock is set by creating a semaphore
- X * file, SG_LOCK.
- X */
- X
- Xint
- Xsgr_lock ()
- X{
- X int fd;
- X int pid;
- X int len;
- X char file[BUFSIZ];
- X char buf[32];
- X struct stat sb;
- X
- X if (islocked)
- X return 1;
- X
- X if (strcmp (sg_filename, SGROUP) != 0)
- X return 0;
- X
- X /*
- X * Create a lock file which can be switched into place
- X */
- X
- X sprintf (file, GR_TEMP, lock_pid = getpid ());
- X if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
- X return 0;
- X
- X sprintf (buf, "%d", lock_pid);
- X if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
- X (void) close (fd);
- X (void) unlink (file);
- X return 0;
- X }
- X close (fd);
- X
- X /*
- X * Simple case first -
- X * Link fails (in a sane environment ...) if the target
- X * exists already. So we try to switch in a new lock
- X * file. If that succeeds, we assume we have the only
- X * valid lock. Needs work for NFS where this assumption
- X * may not hold. The simple hack is to check the link
- X * count on the source file, which should be 2 iff the
- X * link =really= worked.
- X */
- X
- X if (link (file, SG_LOCK) == 0) {
- X if (stat (file, &sb) != 0)
- X return 0;
- X
- X if (sb.st_nlink != 2)
- X return 0;
- X
- X (void) unlink (file);
- X islocked = 1;
- X return 1;
- X }
- X
- X /*
- X * Invalid lock test -
- X * Open the lock file and see if the lock is valid.
- X * The PID of the lock file is checked, and if the PID
- X * is not valid, the lock file is removed. If the unlink
- X * of the lock file fails, it should mean that someone
- X * else is executing this code. They will get success,
- X * and we will fail.
- X */
- X
- X if ((fd = open (SG_LOCK, O_RDWR)) == -1 ||
- X (len = read (fd, buf, BUFSIZ)) <= 0) {
- X errno = EINVAL;
- X return 0;
- X }
- X buf[len] = '\0';
- X if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
- X errno = EINVAL;
- X return 0;
- X }
- X if (kill (pid, 0) == 0) {
- X errno = EEXIST;
- X return 0;
- X }
- X if (unlink (SG_LOCK)) {
- X (void) close (fd);
- X (void) unlink (file);
- X
- X return 0;
- X }
- X
- X /*
- X * Re-try lock -
- X * The invalid lock has now been removed and I should
- X * be able to acquire a lock for myself just fine. If
- X * this fails there will be no retry. The link count
- X * test here makes certain someone executing the previous
- X * block of code didn't just remove the lock we just
- X * linked to.
- X */
- X
- X if (link (file, SG_LOCK) == 0) {
- X if (stat (file, &sb) != 0)
- X return 0;
- X
- X if (sb.st_nlink != 2)
- X return 0;
- X
- X (void) unlink (file);
- X islocked = 1;
- X return 1;
- X }
- X (void) unlink (file);
- X return 0;
- X}
- X
- X/*
- X * sgr_unlock - logically unlock a shadow group file
- X *
- X * sgr_unlock() removes the lock which was set by an earlier
- X * invocation of sgr_lock().
- X */
- X
- Xint
- Xsgr_unlock ()
- X{
- X if (isopen) {
- X open_modes = O_RDONLY;
- X if (! sgr_close ())
- X return 0;
- X }
- X if (islocked) {
- X islocked = 0;
- X if (lock_pid != getpid ())
- X return 0;
- X
- X (void) unlink (SG_LOCK);
- X return 1;
- X }
- X return 0;
- X}
- X
- X/*
- X * sgr_open - open a shadow group file
- X *
- X * sgr_open() encapsulates the open operation. it returns
- X * TRUE or FALSE depending on the shadow group file being
- X * properly opened.
- X */
- X
- Xint
- Xsgr_open (mode)
- Xint mode;
- X{
- X char buf[8192];
- X char *cp;
- X struct sg_file_entry *sgrf;
- X struct sgrp *sgrent;
- X
- X if (isopen || (mode != O_RDONLY && mode != O_RDWR))
- X return 0;
- X
- X if (mode != O_RDONLY && ! islocked &&
- X strcmp (sg_filename, SGROUP) == 0)
- X return 0;
- X
- X if ((sgrfp = fopen (sg_filename, mode == O_RDONLY ? "r":"r+")) == 0)
- X return 0;
- X
- X sgr_head = sgr_tail = sgr_cursor = 0;
- X sgr_changed = 0;
- X
- X while (fgetsx (buf, sizeof buf, sgrfp) != (char *) 0) {
- X if (cp = strrchr (buf, '\n'))
- X *cp = '\0';
- X
- X if (! (sgrf = (struct sg_file_entry *) malloc (sizeof *sgrf)))
- X return 0;
- X
- X sgrf->sgr_changed = 0;
- X sgrf->sgr_line = strdup (buf);
- X if ((sgrent = sgetsgent (buf)) && ! (sgrent = sgr_dup (sgrent)))
- X return 0;
- X
- X sgrf->sgr_entry = sgrent;
- X
- X if (sgr_head == 0) {
- X sgr_head = sgr_tail = sgrf;
- X sgrf->sgr_next = 0;
- X } else {
- X sgr_tail->sgr_next = sgrf;
- X sgrf->sgr_next = 0;
- X sgr_tail = sgrf;
- X }
- X }
- X isopen++;
- X open_modes = mode;
- X
- X return 1;
- X}
- X
- X/*
- X * sgr_close - close the shadow group file
- X *
- X * sgr_close() outputs any modified shadow group file entries and
- X * frees any allocated memory.
- X */
- X
- Xint
- Xsgr_close ()
- X{
- X char backup[BUFSIZ];
- X int mask;
- X int c;
- X int errors = 0;
- X FILE *bkfp;
- X struct sg_file_entry *sgrf;
- X struct stat sb;
- X
- X if (! isopen) {
- X errno = EINVAL;
- X return 0;
- X }
- X if (islocked && lock_pid != getpid ()) {
- X isopen = 0;
- X islocked = 0;
- X errno = EACCES;
- X return 0;
- X }
- X strcpy (backup, sg_filename);
- X strcat (backup, "-");
- X
- X if (open_modes == O_RDWR && sgr_changed) {
- X mask = umask (0277);
- X (void) chmod (backup, 0400);
- X if ((bkfp = fopen (backup, "w")) == 0) {
- X umask (mask);
- X return 0;
- X }
- X umask (mask);
- X fstat (fileno (sgrfp), &sb);
- X chown (backup, sb.st_uid, sb.st_gid);
- X
- X rewind (sgrfp);
- X while ((c = getc (sgrfp)) != EOF) {
- X if (putc (c, bkfp) == EOF) {
- X fclose (bkfp);
- X return 0;
- X }
- X }
- X if (fclose (bkfp))
- X return 0;
- X
- X isopen = 0;
- X (void) fclose (sgrfp);
- X
- X mask = umask (0277);
- X (void) chmod (sg_filename, 0400);
- X if (! (sgrfp = fopen (sg_filename, "w"))) {
- X umask (mask);
- X return 0;
- X }
- X umask (mask);
- X
- X for (sgrf = sgr_head;! errors && sgrf;sgrf = sgrf->sgr_next) {
- X if (sgrf->sgr_changed) {
- X if (putsgent (sgrf->sgr_entry, sgrfp))
- X errors++;
- X } else {
- X if (fputsx (sgrf->sgr_line, sgrfp))
- X errors++;
- X
- X if (putc ('\n', sgrfp) == EOF)
- X errors++;
- X }
- X }
- X if (fflush (sgrfp))
- X errors++;
- X
- X if (errors) {
- X unlink (sg_filename);
- X link (backup, sg_filename);
- X unlink (backup);
- X return 0;
- X }
- X }
- X if (fclose (sgrfp))
- X return 0;
- X
- X sgrfp = 0;
- X
- X while (sgr_head != 0) {
- X sgrf = sgr_head;
- X sgr_head = sgrf->sgr_next;
- X
- X if (sgrf->sgr_entry) {
- X sgr_free (sgrf->sgr_entry);
- X free (sgrf->sgr_entry);
- X }
- X if (sgrf->sgr_line)
- X free (sgrf->sgr_line);
- X
- X free (sgrf);
- X }
- X sgr_tail = 0;
- X isopen = 0;
- X return 1;
- X}
- X
- Xint
- Xsgr_update (sgrent)
- Xstruct sgrp *sgrent;
- X{
- X struct sg_file_entry *sgrf;
- X struct sgrp *nsgr;
- X
- X if (! isopen || open_modes == O_RDONLY) {
- X errno = EINVAL;
- X return 0;
- X }
- X for (sgrf = sgr_head;sgrf != 0;sgrf = sgrf->sgr_next) {
- X if (sgrf->sgr_entry == 0)
- X continue;
- X
- X if (strcmp (sgrent->sg_name, sgrf->sgr_entry->sg_name) != 0)
- X continue;
- X
- X if (! (nsgr = sgr_dup (sgrent)))
- X return 0;
- X else {
- X sgr_free (sgrf->sgr_entry);
- X *(sgrf->sgr_entry) = *nsgr;
- X }
- X sgrf->sgr_changed = 1;
- X sgr_cursor = sgrf;
- X return sgr_changed = 1;
- X }
- X sgrf = (struct sg_file_entry *) malloc (sizeof *sgrf);
- X if (! (sgrf->sgr_entry = sgr_dup (sgrent)))
- X return 0;
- X
- X sgrf->sgr_changed = 1;
- X sgrf->sgr_next = 0;
- X sgrf->sgr_line = 0;
- X
- X if (sgr_tail)
- X sgr_tail->sgr_next = sgrf;
- X
- X if (! sgr_head)
- X sgr_head = sgrf;
- X
- X sgr_tail = sgrf;
- X
- X return sgr_changed = 1;
- X}
- X
- Xint
- Xsgr_remove (name)
- Xchar *name;
- X{
- X struct sg_file_entry *sgrf;
- X struct sg_file_entry *osgrf;
- X
- X if (! isopen || open_modes == O_RDONLY) {
- X errno = EINVAL;
- X return 0;
- X }
- X for (osgrf = 0, sgrf = sgr_head;sgrf != 0;
- X osgrf = sgrf, sgrf = sgrf->sgr_next) {
- X if (! sgrf->sgr_entry)
- X continue;
- X
- X if (strcmp (name, sgrf->sgr_entry->sg_name) != 0)
- X continue;
- X
- X if (sgrf == sgr_cursor)
- X sgr_cursor = osgrf;
- X
- X if (osgrf != 0)
- X osgrf->sgr_next = sgrf->sgr_next;
- X else
- X sgr_head = sgrf->sgr_next;
- X
- X if (sgrf == sgr_tail)
- X sgr_tail = osgrf;
- X
- X return sgr_changed = 1;
- X }
- X errno = ENOENT;
- X return 0;
- X}
- X
- Xstruct sgrp *
- Xsgr_locate (name)
- Xchar *name;
- X{
- X struct sg_file_entry *sgrf;
- X
- X if (! isopen) {
- X errno = EINVAL;
- X return 0;
- X }
- X for (sgrf = sgr_head;sgrf != 0;sgrf = sgrf->sgr_next) {
- X if (sgrf->sgr_entry == 0)
- X continue;
- X
- X if (strcmp (name, sgrf->sgr_entry->sg_name) == 0) {
- X sgr_cursor = sgrf;
- X return sgrf->sgr_entry;
- X }
- X }
- X errno = ENOENT;
- X return 0;
- X}
- X
- Xint
- Xsgr_rewind ()
- X{
- X if (! isopen) {
- X errno = EINVAL;
- X return 0;
- X }
- X sgr_cursor = 0;
- X return 1;
- X}
- X
- Xstruct sgrp *
- Xsgr_next ()
- X{
- X if (! isopen) {
- X errno = EINVAL;
- X return 0;
- X }
- X if (sgr_cursor == 0)
- X sgr_cursor = sgr_head;
- X else
- X sgr_cursor = sgr_cursor->sgr_next;
- X
- X while (sgr_cursor) {
- X if (sgr_cursor->sgr_entry)
- X return sgr_cursor->sgr_entry;
- X
- X sgr_cursor = sgr_cursor->sgr_next;
- X }
- X return 0;
- X}
- END_OF_FILE
- if test 11654 -ne `wc -c <'sgroupio.c'`; then
- echo shar: \"'sgroupio.c'\" unpacked with wrong size!
- fi
- # end of 'sgroupio.c'
- fi
- echo shar: End of archive 4 \(of 11\).
- cp /dev/null ark4isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 11 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-